#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.IO;
using System.Collections;
using System.Globalization;

namespace Yarr
{
	/// <summary>
	/// Definitions of built in Yarr functions
	/// </summary>
    [Package("system")]
	internal static partial class Functions
	{
        private static string SYSTEM = "system";

        /// <summary>
        /// (fn (&rest lists) ...)
        /// Appends lists
        /// </summary>
        /// <param name="args">A collection of lists to be combined</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A combined list</returns>
        [Function("append", "(&rest args)")]
        public static Object Append(Environment environment)
        {
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");

            if (args == null)
                return null;

            if (args.Rest() == null)
            {
                return args.First();
            }
            else
            {
                Cons result;
                Closure self = (Closure)environment.GetValue(Symbol.SELF);

                if (args.First() == null)
                {
                    result = (Cons)self.Invoke((Cons)args.Rest());   
                }
                else
                {
                    result = ((Cons)args.First()).CopyList();
                    ((Cons)result.Last()).Rplacd(self.Invoke((Cons)args.Rest()));
                }
                return result;
            }
        }


		/// <summary>
		/// (fn (function list) ...)
		/// Applies function to a list of arguments. function may be a built-in Yarr function, 
		/// a closure defined by fn a macro defined by macro or the name of a method in the 
		/// .NET framework. 
		/// </summary>
		/// <param name="args">A list, the first element of which is the function to apply.
        /// the rest of the list contain the parameters for the function</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The return value of the function</returns>
		[Function("apply", "(func &rest args)")]
        public static Object Apply(Environment environment) 
		{
            Object func = environment.GetValue(SYSTEM, "func");
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");

            Cons collectedArgs;
            if (args.Length == 1)
                collectedArgs = (Cons)args.Car();
            else
            {
                collectedArgs = new Cons(args.Car());
                args = (Cons)args.Cdr();
                Cons currentArg = collectedArgs;
                while (args.Length > 1)
                {
                    currentArg.SetCdr(new Cons(args.Car()));
                    currentArg = (Cons)currentArg.Cdr();
                    args = (Cons)args.Cdr();
                }
                currentArg.SetCdr(args.Car());
            }

			return Runtime.Apply(func, collectedArgs, environment);
		}


        /// <summary>
        /// (fn (array &rest index) ...)
        /// Get array element
        /// </summary>
        /// <param name="args">A list of arguments. The first is the array; 
        /// the rest of the arguments are the indices into the array</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The specified element of the array</returns>
        [Function("aref", "(arr &rest args)")]
        public static Object Aref (Environment environment)
        {
	        Object arr = environment.GetValue(SYSTEM, "arr");
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");

	        if (!(arr is Array))
	        {
		        if (arr is IList)
			        return ((IList)arr)[(int)args.First()];
		        else
			        throw new YarrException("Aref: First argument must be an array or IList");
	        }

	        int[] index = (int[])args.ToArray();
	        return ((Array)arr).GetValue(index);
        }

        /// <summary>
        /// (fn (&rest objects) ...)
        /// Creates a new array
        /// </summary>
        /// <param name="args">The objects to be placed in the array</param>
        /// <param name="environment">The local environment</param>
        /// <returns>An array of its arguments</returns>
        [Function("array", "(&rest args)")]
        public static Object Array (Environment environment)
        {
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");
            return (args == null) ? new object[0] : args.ToShallowArray();
        }


        /// <summary>
        /// (fn (rank form) ...)
        /// Creates a new multi-dimensional array
        /// </summary>
        /// <param name="args">A list of two items. The first is the rank of the array,
        /// the second is the cons to convert to an array</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A multi-dimensional array</returns>
        [Function("md-array", "((rank System.Int32) (list Yarr.Cons))")]
        public static Object MdArray (Environment environment)
        {
            int rank = (int)environment.GetValue(SYSTEM, "rank");
            Cons list = (Cons)environment.GetValue(SYSTEM, "list");

            return (list == null) ? null : list.ToMdArray(rank);
        }

 
        /// <summary>
		/// (fn (item alist) ...)
		/// finds an item in a list by key value
		/// </summary>
		/// <param name="args">A list. The first item of the list is the key value to search for,
        /// the second item is the list in which to search for the item.</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The first cons in alist whose car is equal to item</returns>
		[Function("assoc", "(item (list Yarr.Cons))")]
        public static Object Assoc(Environment environment) 
		{
            Object item = environment.GetValue(SYSTEM, "item");
            Cons list = (Cons)environment.GetValue(SYSTEM, "list");

			foreach (Cons c in list) 
			{
				if (c.Car().Equals(item))
					return c;
			}

			return null;
		}


		/// <summary>
		/// (fn (list) ...) 
        /// A shorthand for (car (car (car list)))
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CAAAR of the list</returns>
        [Function("caaar", "((list Yarr.Cons))")]
        public static Object Caaar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Caaar();
		}


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (car (car (cdr list)))  
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CAADR of the list</returns>
        [Function("caadr", "((list Yarr.Cons))")]
        public static Object Caadr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Caadr();
		}


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (car (car list))
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CAAR of the list</returns>
        [Function("caar", "((list Yarr.Cons))")]
        public static Object Caar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Caar();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (car (cdr (car list)))
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CADAR of the list</returns>
        [Function("cadar", "((list Yarr.Cons))")]
        public static Object Cadar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cadar();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (car (cdr (cdr list))) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CADDR of the list</returns>
        [Function("caddr;third", "((list Yarr.Cons))")]
        public static Object Caddr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Caddr();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (car (cdr list)) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CADR of the list</returns>
        [Function("cadr;second", "((list Yarr.Cons))")]
        public static Object Cadr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cadr();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// Returns the first element of a list
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The first element in the list</returns>
        [Function("car;first", "((list Yarr.Cons))")]
        public static Object Car(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Car();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (car (car x))) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CDAAR of the list</returns>
        [Function("cdaar", "((list Yarr.Cons))")]
        public static Object Cdaar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cdaar();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (car (cdr x))) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CDADR of the list</returns>
        [Function("cdadr", "((list Yarr.Cons))")]
        public static Object Cdadr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cdadr();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (car x)) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CDAR of the list</returns>
        [Function("cdar", "((list Yarr.Cons))")]
        public static Object Cdar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cdar();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (cdr (car x))) 
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CDDAR of the list</returns>
        [Function("cddar", "((list Yarr.Cons))")]
        public static Object Cddar(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cddar();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (cdr (cdr list)))
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local envrionment</param>
		/// <returns>The CDDDR of the list</returns>
        [Function("cdddr", "((list Yarr.Cons))")]
        public static Object Cdddr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cdddr();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// A shorthand for (cdr (cdr list))
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The CDDR of the list</returns>
        [Function("cddr", "((list Yarr.Cons))")]
        public static Object Cddr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cddr();
        }


		/// <summary>
        /// (fn (list) ...) 
        /// Returns the Cdr (second part) of a cons
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The list, minus the first element</returns>
        [Function("cdr;rest", "((list Yarr.Cons))")]
        public static Object Cdr(Environment environment) 
		{
            Object o = environment.GetValue(SYSTEM, "list");

            return (o == null) ? null : ((Cons)o).Cdr();
        }


		/// <summary>
        /// (fn (car cdr) ...)
		/// Creates a fresh cons
		/// </summary>
		/// <param name="args">A list with two elements. The first will become the CAR of the new list,
        /// and the second will become the CDR of the new list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>A new cons</returns>
		[Function("cons", "(car cdr)")]
        public static Object Cons(Environment environment) 
		{
            return new Cons(environment.GetValue(SYSTEM, "car"), environment.GetValue(SYSTEM, "cdr"));
		}


        /// <summary>
        /// (fn (list) ...)
        /// Returns a shallow copy of the list given as its argument
        /// </summary>
        /// <param name="args">The list to copy</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A shallow copy of the list</returns>
        [Function("copylist", "((list Yarr.Cons))")]
        public static Object CopyList(Environment environment) 
        {
            return ((Cons)environment.GetValue(SYSTEM, "list")).CopyList();
        }


        /// <summary>
        /// (fn (containier index) ...)
        /// Returns the specified element from a .NET container
        /// </summary>
        /// <param name="args">
        /// A list; the first element is a .NET array or IDictionary, the rest is the key
        /// or index to get
        /// </param>
        /// <param name="environment">the local environment</param>
        /// <returns>The element at the specified index of the container</returns>
        [Function("elt", "(container &rest args)")]
        public static Object Elt (Environment environment)
        {
            Object container = environment.GetValue(SYSTEM, "container");
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");
            
            Closure aref = (Closure)environment.GetValue(Symbol.AREF);

            if (container != null && (container is IDictionary))
            {
                Object key = args.First();

                return (((IDictionary)container).Contains(key)) ?
                                ((IDictionary)container)[key] : null;
            }

            return aref.Invoke(new Cons(container, args));
        }


		/// <summary>
		/// (fn (form) ...)
        /// Evaluates an Yarr expression in a given environment
		/// </summary>
		/// <param name="args">The expression to evaluate</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The result of the evaluation</returns>
		[Function("eval", "(expr)")]
        public static Object Eval(Environment environment) 
		{
            return Runtime.Eval(environment.GetValue(SYSTEM, "expr"), environment);
		}


		/// <summary>
        /// (fn (string) ...)
		/// Evaluates an Yarr expression contained in a string within a given
		/// environment
		/// </summary>
		/// <param name="args">A list containing the string to evaluate</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The result of the evaluation</returns>
		[Function("evalstring", "((expr System.String))")]
        public static Object EvalString(Environment environment) 
		{
            return Runtime.EvalString((string)environment.GetValue(SYSTEM, "expr"), environment);		
		}


        /// <summary>
        /// (fn (predicate &rest list) ...)
        /// Evaluates predicate, using the 0'th element of each list as arguments. As long
        /// as this evalutas true, continues evaluating preicate, using each subsequent list
        /// element as the argument.
        /// </summary>
        /// <param name="args">
        /// The first element is a function, the remainder are lists of arguments
        /// for the fuction, one list for each argument expected
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>True if the function evaluates true for every invocation</returns>
        [Function("every","(predicate &rest args)")]
        public static object Every (Environment environment)
        {
            return PredicateMapHelper(false, false, environment.GetValue(SYSTEM, "predicate"),
                                                    (Cons)environment.GetValue(SYSTEM, "args"), environment);
        }

		/// <summary>
		/// (fn (&optional exit-code) ...)
		/// Terminates the current process and returns the specified exit-code to the 
		/// calling process. If the symbol *no-exit* is present in the environemnt,
        /// regardless of its value, then the exit funciton will do nothing.
		/// </summary>
		/// <param name="args">A list containing the exit-code</param>
		/// <param name="environment">The local environment</param>
		/// <returns>Does not return - terminates the current process</returns>
        [Function("exit","(&optional ((exit-code System.Int32)))")]
        public static Object Exit(Environment environment) 
		{
            int exitCode = (int)environment.GetValue(SYSTEM, "exit-code");

            if (!environment.Contains(SYSTEM, "*no-exit*"))
                System.Environment.Exit(exitCode);
            else
                throw new YarrException("Exit: not permitted");

            return null;
		}


        /// <summary>
        /// (fn () ...)
        /// Generates a new symbol
        /// </summary>
        /// <param name="args">Ignored</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A generated symbol</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "environment")]
        [Function("gensym")]
        public static Object Gensym(Environment environment)
        {
            return Symbol.FromName("G" + Guid.NewGuid());
        }


        /// <summary>
        /// (fn (symbol property) ...)
        /// Get a symbol's property value
        /// </summary>
        /// <param name="args">
        /// A list with two elements. The first is the symbol, 
        /// the second is the property value to get
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>The property value</returns>
        [Function("get", "((sym Yarr.Symbol) prop)")]
        public static Object Get (Environment environment)
        {
            Symbol sym = (Symbol)environment.GetValue(SYSTEM, "sym");
            Object prop = environment.GetValue(SYSTEM, "prop");

            if (prop == null  ||  !(prop is Symbol  ||  prop is String))
                throw new YarrException("Get: Second argument must be a property name");

            Symbol property = (prop is Symbol) ? (Symbol)prop : Symbol.FromName((String)prop);

            return environment.GetProperty(sym, property);
        }


        /// <summary>
        /// (fn (target eventName handler) ...)
        /// Sets up a new event handler for events named eventName on target. The
        /// handler should be an Yarr closure with two arguments, the sender and the
        /// event arguments, for example: (defun handler (sender args) (prl "Event Handled")).
        /// </summary>
        /// <param name="args"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("handle-event", "(target event-name (handler Yarr.Closure))")]
        public static Object HandleEvent(Environment environment)
        {
            Object target = environment.GetValue(SYSTEM, "target");
            Object event_name = environment.GetValue(SYSTEM, "event-name");
            Closure handler = (Closure)environment.GetValue(SYSTEM, "handler");

            string evtName;

            if (event_name is String)
                evtName = (string)event_name;
            else if (event_name is Symbol)
                evtName = ((Symbol)event_name).Name;
            else
                throw new YarrException(String.Format(CultureInfo.CurrentCulture, 
                            "Handle-event: {0} is not an event name", event_name));

            return EventAdapter.AddEventHandler(target, evtName, handler);
        }


        /// <summary>
        /// (fn (obj) ...)
        /// The identity funciton - returns its argument
        /// </summary>
        /// <param name="args"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("identity","(&optional arg)")]
        public static Object Identity (Environment environment)
        {
            return environment.GetValue(SYSTEM, "arg");
        }


		/// <summary>
		/// (fn (object) ...)
        /// Returns a description of the specified object, using reflection. 
		/// Useful for debugging. 
		/// </summary>
		/// <param name="args">A list containing the object to inspect</param>
		/// <param name="environment">The local environment</param>
		/// <returns>Returns null</returns>
		[Function("inspect", "(obj &optional ((writer system.io.textwriter) *output*))")]
        public static Object Inspect(Environment environment) 
		{
            Object obj = environment.GetValue(SYSTEM, "obj");
            TextWriter con = (TextWriter)environment.GetValue(SYSTEM, "writer");

            if (con != null)
                con.WriteLine(Inspector.Inspect(obj));

            return null;
        }

        /// <summary>
        /// (fn (pkg) ...)
        /// Sets the current (default) package
        /// </summary>
        /// <param name="environment">The local environment</param>
        /// <returns>The package that was set</returns>
        [Function("in-package", "(pkg)")]
        public static Object InPackage(Environment environment)
        {
            Object pkg = environment.GetValue(SYSTEM, "pkg");

            if (pkg is Package)
            {
                Symbol.CurrentPackage = (Package)pkg;
                return pkg;
            }

            if (pkg is Symbol || pkg is String)
            {
                Package p = Package.FindPackage(pkg.ToString());
                if (p == null)
                    throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                            "InPackage: No such package {0}", pkg));

                Symbol.CurrentPackage = p;
                return p;
            }
            else
                throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                        "InPackage: Expecting a package or package name, but received {0}", pkg));
        }

		/// <summary>
		/// (fn (type object) ...)
		/// Determine whether the run-time type of an object is 
		/// compatible with a given type.
		/// </summary>
		/// <param name="args">A list with two items. The first is a type, the second is any object</param>
		/// <param name="environment">The local environment</param>
		/// <returns>True if the specified object is compatible with the given type</returns>
		[Function("is", "(type obj)")]
        public static Object Is(Environment environment) 
		{
            Object type = environment.GetValue(SYSTEM, "type");
            Object obj = environment.GetValue(SYSTEM, "obj");

			return Primitives.IsTypeOf(type, obj);
		}


		/// <summary>
		/// (fn (object) ...)
		/// Returns the length of expression. If expression is null, length returns 0, 
		/// otherwise the length is calculated by calling the length method on the object, 
		/// ensuring that length works for strings, lists and most collection-like objects. 
		/// </summary>
		/// <param name="args">A list containing the object</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The length of the object</returns>
		[Function("length", "(obj)")]
        public static Object Length(Environment environment) 
		{
            Object obj = environment.GetValue(SYSTEM, "obj");

			if (obj == null)
				return 0;

            if (obj is Cons)
                return ((Cons)obj).Length;

            if (obj is String)
                return ((String)obj).Length;

            if (obj is Array)
                return ((Array)obj).Length;

            if (obj is ICollection)
                return ((ICollection)obj).Count;

            else
                return Runtime.Call("length", obj, null, environment);
		}


		/// <summary>
		/// (fn (&rest object) ...)
		/// Creates a new cons, an ordered list with each object as a member. Note that
        /// the list has already been created by the reader before we get here, so this
        /// really just returns its argument
		/// </summary>
		/// <param name="args">A list</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The args list</returns>
        [Function("list", "(&rest args)")]
		public static Object List(Environment environment) 
		{
            return environment.GetValue(SYSTEM, "args");
		}


        /// <summary>
        /// (fn (form) ...)
        /// Repeatedly expands a macro using a list of arguments
        /// </summary>
        /// <param name="args">A list, the first element of which is the macro to expand, 
        /// and the rest of which are the arugments for the macro</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The expanded macro</returns>
        [Function("macroexpand", "(form)")]
        public static Object MacroExpand (Environment environment)
        {
            Object form = environment.GetValue(SYSTEM, "form");
            Closure macExp1 = (Closure)environment.GetValue(Symbol.MACEXP1);

            if (form == null || !(form is Cons))
                return form;

            Object result =  form;
            Object next = null;

            while (next != result)
            {
                next = result;
                result = macExp1.Invoke(new Cons(next));
            }

            return result;
        }

        /// <summary>
        /// (fn (pkg &key use shadow) ...)
        /// Create or modify a package
        /// </summary>
        /// <param name="environment">The local environment</param>
        /// <returns>The package</returns>
        [Function("make-package", "(pkg &key ((use yarr.cons)) ((shadow yarr.cons)))")]
        public static Object MakePackage(Environment environment)
        {
            Package package = Package.MakePackage(environment.GetValue(SYSTEM, "pkg").ToString());

            if (package != null)
            {
                Cons usePkgs = (Cons)environment.GetValue(SYSTEM, "use");
                if (usePkgs != null)
                {
                    foreach (object p in usePkgs)
                    {
                        if (p is Symbol || p is String)
                        {
                            Package pkg = Package.FindPackage(p.ToString());
                            if (pkg != null)
                                package.UsePackage(pkg);
                            else
                                throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                        "MakePackage: No such package {0}", p));
                        }
                        else
                            throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "MakePackage: Expecting a package name, but received {0}", p));
                    }
                }

                Cons shadows = (Cons)environment.GetValue(SYSTEM, "shadow");
                if (shadows != null)
                {
                    foreach (object s in shadows)
                    {
                        if (s is Symbol || s is String)
                            package.Shadow(s.ToString());
                        else
                            throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "MakePackage: Expecting a Symbol or Symbol name, but received {0}", s));
                    }
                }
            }

            return package;
        }

        /// <summary>
        /// (fn (form) ...)
        /// Expands a macro
        /// </summary>
        /// <param name="args">The macro to expand</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The expanded macro</returns>
        [Function("macroexpand-1", "(form)")]
        public static Object MacroExpand_1(Environment environment) 
		{
            Object args = environment.GetValue(SYSTEM, "form");

            if (args == null || !(args is Cons))
                return args;


            Cons form = (Cons)args;
            Object macro = null;

            if (form.First() is Macro)
                macro = form.First();
            
            else if (form.First() is Symbol)
                macro = environment.GetValue((Symbol)form.First());

            if (macro != null && (macro is Macro))
                args = ((Macro)macro).Expand((Cons)form.Rest());

            return args;
		}


		/// <summary>
		/// (fn (function &rest lists) ...)
        /// Maps function to each element in list
		/// </summary>
		/// <param name="args">A list. The first element is the function to apply,
        /// and the remaining elements are IEnumerable arguments</param>
		/// <param name="environment">The local environment</param>
		/// <returns>A list containing the results of applying the function to each set of arguments</returns>
        [Function("map;mapcar", "(func &rest lists)")]
        public static Object Map (Environment environment)
        {
            return MapcHelper(true, environment.GetValue(SYSTEM, "func"),
                                    (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        /// <summary>
        /// (fn (function &rest lists) ...)
        /// Maps function to each element in list
        /// </summary>
        /// <param name="args">A list. The first element is the function to apply,
        /// and the remaining elements are IEnumerable arguments</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The first argument list</returns>
        [Function("mapc", "(func &rest lists)")]
        public static Object Mapc (Environment environment)
        {
            return MapcHelper(false, environment.GetValue(SYSTEM, "func"),
                                     (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        private static Object MapcHelper (bool collectResults, Object func, Cons args, Environment environment) 
		{
            Object result = null;

            if (args.Length == 0)
                return collectResults ? Runtime.Apply(func, null, environment) : null;

            if (!collectResults)
                result = args.Car();

            int ix, len = args.Length;
            IEnumerator[] argLists = new IEnumerator[len];
            for (ix = 0; ix < len; ++ix)
            {
                Object arg = args.First();
                if (arg == null)
                    return null;

                if (!(arg is IEnumerable))
                    throw new YarrException("Map: all arguments must be IEnumerable");

                argLists[ix] = ((IEnumerable)arg).GetEnumerator();
                args = (Cons)args.Rest();
            }

            bool done = false;
            while (!done)
            {
                Cons temp = null;
                for (ix = len - 1; ix >= 0; --ix)
                {
                    if (!argLists[ix].MoveNext())
                    {
                        done = true;
                        break;
                    }

                    temp = new Cons(argLists[ix].Current, temp);
                }

                if (!done)
                {
                    if (collectResults)
                        result = new Cons(Runtime.Apply(func, temp, environment), result);
                    else
                        Runtime.Apply(func, temp, environment);
                }
            }

            return collectResults ? ((Cons)result).NReverse() : result;
		}


        /// <summary>
        /// (fn (function &rest lists) ...)
        /// Maps function to each sublist in list
        /// </summary>
        /// <param name="args">A list. The first element is the function to apply,
        /// and the remaining elements are lists of arguments</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A list containing the results of applying the function to each set of arguments</returns>
        [Function("maplist", "(func &rest lists)")]
        public static Object MapList (Environment environment)
        {
            return MaplHelper(true, environment.GetValue(SYSTEM, "func"),
                                    (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        /// <summary>
        /// (fn (function &rest lists) ...)
        /// Maps function to each sublist in list
        /// </summary>
        /// <param name="args">A list. The first element is the function to apply,
        /// and the remaining elements are lists of arguments</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The first argument list</returns>
        [Function("mapl", "(func &rest lists)")]
        public static Object Mapl (Environment environment)
        {
            return MaplHelper(false, environment.GetValue(SYSTEM, "func"),
                                     (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        private static Object MaplHelper (bool collectResults, Object func, Cons args, Environment environment)
        {
            Object result = null;

            if (args.Length == 0)
                return collectResults ? Runtime.Apply(func, null, environment) : null;

            if (!collectResults)
                result = args.Car();

            int ix, len = args.Length;
            Cons[] argLists = new Cons[len];
            for (ix = 0; ix < len; ++ix)
            {
                Object arg = args.First();

                if (!(arg is Cons))
                    throw new YarrException("Maplist: all arguments must be lists");

                argLists[ix] = (Cons)arg;
                args = (Cons)args.Rest();
            }

            bool done = false;
            while (!done)
            {
                Cons temp = null;
                for (ix = len - 1; ix >= 0; --ix)
                {
                    if (argLists[ix] == null)
                    {
                        done = true;
                        break;
                    }

                    temp = new Cons(argLists[ix], temp);
                    argLists[ix] = (Cons)argLists[ix].Rest();
                }

                if (!done)
                {
                    if (collectResults)
                        result = new Cons(Runtime.Apply(func, temp, environment), result);
                    else
                        Runtime.Apply(func, temp, environment);
                }
            }

            return collectResults ? ((Cons)result).NReverse() : result;
        }


        private static Object PredicateMapHelper (bool terminalValue, bool invert, 
                                                    Object func, Cons args, Environment environment)
        {
            if (args.Length == 0)
                return Conversions.ObjectToBoolean(Runtime.Apply(func, null, environment)) != invert;

            int ix, len = args.Length;
            IEnumerator[] argLists = new IEnumerator[len];
            for (ix = 0; ix < len; ++ix)
            {
                Object arg = args.First();

                if (arg == null)
                    return Conversions.ObjectToBoolean(Runtime.Apply(func, null, environment)) != invert;

                if (!(arg is IEnumerable))
                    throw new YarrException("PredicateMap: Arguments must be IEnumerable");

                argLists[ix] = ((IEnumerable)arg).GetEnumerator();
                args = (Cons)args.Rest();
            }

            bool result = !terminalValue;
            bool done = false;

            while (result != terminalValue   &&  !done)
            {
                Cons temp = null;
                for (ix = len - 1; ix >= 0; --ix)
                {
                    if (!argLists[ix].MoveNext())
                    {
                        done = true;
                        break;
                    }
                    temp = new Cons(argLists[ix].Current, temp);
                }

                if (!done)
                    result = Conversions.ObjectToBoolean(Runtime.Apply(func, temp, environment));
            }

            return result != invert;
        }


        /// <summary>
        /// (fn (item list) ...)
        /// Determines if item is a member of list
        /// </summary>
        /// <param name="args">
        /// A list with two elements, the first element of which is the item, 
        /// and the second is any IEnumerable object
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>if the object exists in the list returns the object, false otherwise</returns>
        [Function("member", "(item list)")]
        public static Object Member(Environment environment)
        {
            object list = environment.GetValue(SYSTEM, "list");
            if (list == null)
                return null;

            object value = environment.GetValue(SYSTEM, "item");

            if (list is IDictionary)
                return ((IDictionary)list).Contains(value) ? value : null;

            if (list is IList)
                return ((IList)list).Contains(value) ? value : null;

            if (list is IEnumerable)
            {
                foreach (object o in (IEnumerable)list)
                {
                    if (Primitives.Eql(o, value))
                        return value;
                }
            }
            else
                throw new YarrException("Member: expecting second argument to be IEnumerable");

            return null;
        }


        /// <summary>
        /// (fn (&rest lists) ...)
        /// Destructively concatenats lists 
        /// </summary>
        /// <param name="args">A list of lists to be concatenated</param>
        /// <param name="environment">The local environment</param>
        /// <returns>Returns a list whose elements are the elements of each list in
        /// order. Destructively modifies all but the last list, such that
        /// the cdr of the last cons in each list is the next list.
        /// </returns>
        [Function("nconc","(&rest lists)")]
        public static Object Nconc(Environment environment)
        {
            Cons args = (Cons)environment.GetValue(SYSTEM, "lists");

            // With no argument, returns null
            if (args == null)
                return null;

            Object start = args.Car(); // "pointer" to start of list

            if (!(start is Cons))
                throw new YarrException("Nconc: all arguments must be lists");

            Object next = args.Cdr();

            while (next != null)
            {
                if (!(next is Cons))
                    throw new YarrException("Nconc: all arguments must be lists");

                Object end = ((Cons)start).Last();
                if (!(end is Cons))
                    throw new YarrException("Nconc: all arguments must be lists");

                ((Cons)end).Rplacd(((Cons)next).Car());
                next = ((Cons)next).Cdr();
            }

            return start;
        }


		/// <summary>
		/// (fn (class &rest parameters) ...) 
        /// Creates a new object, an instance of type class 
		/// </summary>
		/// <param name="args">A list, the first item of which is the type to create.
        /// The rest of the list is the set of parameters to pass to the class constructor
        /// </param>
		/// <param name="environment">The local environment</param>
		/// <returns>An instance of the specified class</returns>
		[Function("new","((class System.Type) &rest parameters)")]
        public static Object New(Environment environment) 
		{
            return Runtime.MakeInstance((Type)environment.GetValue(SYSTEM, "class"), 
                                        (Cons)environment.GetValue(SYSTEM, "parameters"));
		}


        /// <summary>
        /// (fn (predicate &rest lists) ...)
        /// Evaluates predicate, using the 0'th element of each list as arguments. As long
        /// as this evalutas false, continues evaluating predicate, using each subsequent list
        /// element as the argument.
        /// </summary>
        /// <param name="args">
        /// The first element is a function, the remainder are lists of arguments
        /// for the fuction, one list for each argument expected
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>True if the function evaluates false for every invocation</returns>
        [Function("notany", "(predicate &rest lists)")]
        public static Object NotAny (Environment environment)
        {
            return PredicateMapHelper(true, true, environment.GetValue(SYSTEM, "predicate"),
                                                  (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        /// <summary>
        /// (fn (predicate &rest lists) ...)
        /// Evaluates predicate, using the 0'th element of each list as arguments. As long
        /// as this evalutas true, continues evaluating predicate, using each subsequent list
        /// element as the argument.
        /// </summary>
        /// <param name="args">
        /// The first element is a function, the remainder are lists of arguments
        /// for the fuction, one list for each argument expected
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>False if the function evaluates true for every invocation</returns>
        [Function("notevery", "(predicate &rest lists)")]
        public static Object NotEvery (Environment environment)
        {
            return PredicateMapHelper(false, true, environment.GetValue(SYSTEM, "predicate"),
                                                   (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

		/// <summary>
		/// (fn (n list) ...) 
        /// Find an element in an IEnumerable collection (0-based)
		/// </summary>
		/// <param name="args">A list. the first element is the index, 
        /// the second is any IEnumerable instance</param>
		/// <param name="environment">The local environment</param>
        /// <returns>Returns the (n+1)th element of the IEnumerable collection
        /// </returns>
		[Function("nth","((index System.Int32) seq)")]
        public static Object Nth(Environment environment) 
		{
            int index = (int)environment.GetValue(SYSTEM, "index");
            Object seq = environment.GetValue(SYSTEM, "seq");

            if (seq is Cons)
                return ((Cons)seq).Nth(index);

            if (seq is IList)
                return ((IList)seq)[index];

			if (seq is IEnumerable) 
			{
				IEnumerator e = ((IEnumerable) seq).GetEnumerator();
				for (int i = 0; i <= index; i ++ ) 
				{
					e.MoveNext();
				}
				return e.Current;
			}

            throw new YarrException(string.Format(CultureInfo.CurrentCulture, 
                                        "Nth: {0} is not IEnumerable", seq));
		}

        /// <summary>
        /// (fn (obj) ...)
        /// Determine if the argument is a number
        /// </summary>
        /// <param name="args"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("numberp","(obj)")]
        public static Object NumberP (Environment environment)
        {
            return Primitives.IsNumber(environment.GetValue(SYSTEM, "obj"));
        }

		/// <summary>
		/// (fn (&rest objects) ...) 
        /// Prints each object to *output*, without a new line. 
        /// If *output* is null, does nothing.
		/// </summary>
		/// <param name="args">A list of objects to print</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The value of the last parameter</returns>
        [Function("pr","(&rest objects)")]
		public static Object Pr(Environment environment) 
		{
            return PrHelper(false, (Cons)environment.GetValue(SYSTEM, "objects"), environment);
		}

		/// <summary>
        /// (fn (&rest objects) ...) 
        /// Prints each object to *output*, then prints a new line.
        /// If *output* is null, does nothing.
        /// </summary>
		/// <param name="args">A list of objects to print</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The value of the last parameter</returns>
        [Function("prl", "(&rest objects)")]
        public static Object Prl(Environment environment) 
		{
            return PrHelper(true, (Cons)environment.GetValue(SYSTEM, "objects"), environment);
        }

        private static Object PrHelper (bool terPri, Cons args, Environment environment)
        {
            bool writeDebug = false, writeTrace = false;

            TextWriter con = null;
            if (args != null)
            {
                if (args.Car() is TextWriter)
                {
                    con = (TextWriter)args.Car();
                    args = (Cons)args.Rest();
                }
                if (args.Car() is Symbol)
                {
                    if (args.Car() == Symbol.TRACE)
                    {
                        writeTrace = true;
                        args = (Cons)args.Rest();
                    }
                    else if (args.Car() == Symbol.DEBUG)
                    {
                        writeDebug = true;
                        args = (Cons)args.Rest();
                    }
                }
            }

            if (con == null  &&  !writeDebug  &&  !writeTrace)
            {
                if (environment == null)
                    throw new YarrException("Pr: Environment is null");
                object output = environment.GetValue(Symbol.OUTPUT);

                if (output == null)
                    return null;

                if (output is TextWriter)
                    con = (TextWriter)output;
                else if (output is Symbol  &&  output == Symbol.TRACE)
                    writeTrace = true;
                else if (output is Symbol  &&  output == Symbol.DEBUG)
                    writeDebug = true;
                else
                    return null;
            }

            object result = null;

            if (args == null)
            {
                if (con != null)
                    con.WriteLine("null");
                else if (writeTrace)
                    System.Diagnostics.Trace.WriteLine("null");
                else if (writeDebug)
                    Runtime.Profiler.Comment ("null", environment);
            }
            else
            {
                foreach (object o in args)
                {
                    result = o;
                    object arg = o;

                    if (arg is System.String)
                    {
                        if (con != null)
                            con.Write(arg);
                        else if (writeTrace)
                            System.Diagnostics.Trace.Write(arg);
                        else if (writeDebug)
                            Runtime.Profiler.Comment((String)arg, environment);
                    }
                    else
                    {
                        if (con != null)
                            con.Write(Printer.WriteToString(arg));
                        else if (writeTrace)
                            System.Diagnostics.Trace.Write(Printer.WriteToString(arg));
                        else if (writeDebug)
                            Runtime.Profiler.Comment(Printer.WriteToString(arg), environment);
                    }
                }
            }

            if (terPri)
            {
                if (con != null)
                    con.WriteLine();
                else if (writeTrace)
                {
                    System.Diagnostics.Trace.WriteLine(String.Empty);
                    System.Diagnostics.Trace.Flush();
                }
                else if (writeDebug)
                    Runtime.Profiler.Comment("\n", environment);
            }

            return result;
        }


		/// <summary>
		/// (fn (TextReader &optional eof) ...)
		/// </summary>
		/// <param name="args"></param>
		/// <param name="environment"></param>
		/// <returns></returns>
		[Function("read","(&optional ((textReader System.IO.TextReader) *input*) eof)")]
        public static Object Read(Environment environment) 
		{
            return Runtime.Read((TextReader)environment.GetValue(SYSTEM, "textReader"),
                                            environment.GetValue(SYSTEM, "eof"), environment);		
		}


		/// <summary>
		/// (fn (&rest names) ...)
        /// Loads the specified .NET assembly, either from the GAC, 
		/// local directory or from and explicit directory. 
		/// name may either be a fully qualified filename or a partial assembly name. 
		/// </summary>
		/// <param name="args">A list of assembly names</param>
		/// <param name="environment">The local environemnt</param>
		/// <returns>The result of the final LoadAssembly call</returns>
		[Function("reference","(&rest names)")]
        public static Object Reference(Environment environment) 
		{
            Cons names = (Cons)environment.GetValue(SYSTEM, "names");
			Object result = null;

            if (names != null)
            {
                foreach (Object module in names)
                {
                    Object moduleName = (module is Symbol) ? module.ToString() : module;

                    if (moduleName is string)
                        result = Runtime.LoadAssembly((string)moduleName);
                    else
                        throw new YarrException(String.Format(CultureInfo.CurrentCulture, 
                                    "Reference: {0} is not an assembly", module));
                }
            }

			return result;
		}


        /// <summary>
        /// (fn (func seq &key key start end from-end initial-value) ...)
        /// Like the Common Lisp reduce function
        /// </summary>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("reduce", 
            @"(func seq &key (key identity) 
                             ((start System.Int32) 0) 
                             ((end System.Int32) (- (length seq) 1))
                             (from-end false)
                             initial-value)")]
        public static Object Reduce(Environment environment)
        {
            Object func = environment.GetValue(SYSTEM, "func");
            Object seq = environment.GetValue(SYSTEM, "seq");
            Object key = environment.GetValue(SYSTEM, "key");
            int start = (int)environment.GetValue(SYSTEM, "start");
            int end = (int)environment.GetValue(SYSTEM, "end");
            bool fromEnd = Conversions.ObjectToBoolean(environment.GetValue(SYSTEM, "from-end"));
            Object initialValue = environment.GetValue(SYSTEM, "initial-value");

            Closure nth = (Closure)environment.GetValue(Symbol.NTH);

            if (initialValue == null)
            {
                if (seq != null)
                {
                    if (fromEnd)
                        initialValue = Runtime.Apply(key, new Cons(nth.Invoke(new Cons(end--, new Cons(seq)))), environment);
                    else
                        initialValue = Runtime.Apply(key, new Cons(nth.Invoke(new Cons(start++, new Cons(seq)))), environment);
                }
            }

            if (seq == null)
                return (initialValue == null) ? Runtime.Apply(func, null, environment) : initialValue;

            while (start <= end)
            {
                initialValue = fromEnd ? 
                        Runtime.Apply(func, new Cons(Runtime.Apply(key, 
                                                                   new Cons(nth.Invoke(new Cons(end--, new Cons(seq)))), 
                                                                   environment),
                                            new Cons(initialValue)), environment) :
                        Runtime.Apply(func, new Cons(initialValue,
                                            new Cons(Runtime.Apply(key, 
                                                                   new Cons(nth.Invoke(new Cons(start++, new Cons(seq)))), 
                                                                   environment))),
                                                                   environment);
            }
            
            return initialValue;
        }


        /// <summary>
		/// (fn () ...) 
        /// Resets the global environment and clears the type cache
		/// </summary>
		/// <param name="args">Ignored</param>
		/// <param name="environment">The local environment</param>
		/// <returns>Alwasy returns null</returns>
		[Function("reset")]
        public static Object Reset(Environment environment) 
		{
			environment.GlobalReset();
			TypeCache.Clear();
			return null;
		}

        /// <summary>
        /// (fn (list) ...)
        /// Revese the order of the list; leaves original list unchanged
        /// </summary>
        /// <param name="args">One element - the list to be reversed</param>
        /// <param name="environment">The local environment</param>
        /// <returns>A copy of the list with the elements reversed</returns>
        [Function("reverse", "((list Yarr.Cons))")]
        public static Object Reverse (Environment environment)
        {
            return ((Cons)environment.GetValue(SYSTEM, "list")).Reverse();
        }


        /// <summary>
        /// (fn (list) ...)
        /// Destructively reverse a list
        /// </summary>
        /// <param name="args">One element - the list to be reversed</param>
        /// <param name="environment">The local environment</param>
        /// <returns>The list with the items reversed</returns>
        [Function("nreverse", "((list Yarr.Cons))")]
        public static Object NReverse (Environment environment)
        {
            return ((Cons)environment.GetValue(SYSTEM, "list")).NReverse();
        }

        /// <summary>
        /// (fn (symbols &optional ((pkg yarr.package)) ...)
        /// Ensure that package contains the specified symbols
        /// </summary>
        /// <param name="environment"The local environment></param>
        /// <returns>Returns true unless an exception is thrown</returns>
        [Function("shadow", "(symbols &optional ((pkg yarr.package)))")]
        public static object Shadow(Environment environment)
        {
            Package pkg = (Package)environment.GetValue(SYSTEM, "pkg");
            if (pkg == null)
                pkg = Symbol.CurrentPackage;

            Object syms = environment.GetValue(SYSTEM, "symbols");

            if (syms is Symbol || syms is String)
                pkg.Shadow(syms.ToString());
            else if (syms is Cons)
            {
                foreach (Object s in (Cons)syms)
                {
                    if (s is Symbol || s is String)
                        pkg.Shadow(s.ToString());
                    else
                        throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "Shadow: Expecting symbol name, but received {0}", s));
                }
            }
            else
                throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                           "Shadow: Expecting symbol name(s) but received {0}", syms));
            return true;
        }

        /// <summary>
        /// (fn (predicate &rest list) ...)
        /// Evaluates predicate, using the 0'th element of each list as arguments. As long
        /// as this evalutas false, continues evaluating predicate, using each subsequent list
        /// element as the argument.
        /// </summary>
        /// <param name="args">
        /// The first element is a function, the remainder are lists of arguments
        /// for the fuction, one list for each argument expected
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>False if the function evaluates false for every invocation</returns>
        [Function("some","(predicate &rest lists)")]
        public static object Some (Environment environment)
        {
            return PredicateMapHelper(true, false, environment.GetValue(SYSTEM, "predicate"),
                                                   (Cons)environment.GetValue(SYSTEM, "lists"), environment);
        }

        
        /// <summary>
        /// (fn (type value) ...)
        /// Returns value converted or cast to an object of the specified type. 
        /// Throws an exception is the cast is not achievable. 
        /// The allows type casting and type conversion. This is much more than 
        /// a wrapper for the System.Convert class, it has special meaning for 
        /// conversions to and from Lists and certain common .NET data structures.
        /// </summary>
        /// <param name="args">
        /// A list of two elements. The first is the type to convert to,
        /// the second is the value to be converted
        /// </param>
        /// <param name="environment">The local environment</param>
        /// <returns>The converted type.</returns>
        [Function("the", "((type System.Type) value)")]
        public static Object The (Environment environment)
        {
            return Conversions.The((Type)environment.GetValue(SYSTEM, "type"), environment.GetValue(SYSTEM, "value"));
        }

        /// <summary>
        /// Creates a concrete type from a generic type
        /// </summary>
        /// <param name="args"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("type", "(base-type &rest args)")]
        public static Object MakeType(Environment environment)
        {
            string typeName = environment.GetValue(SYSTEM, "base-type").ToString();
            Cons args = (Cons)environment.GetValue(SYSTEM, "args");

            Type[] typeArray = null;
            if (args != null)
                typeArray = (Type[])args.ToArray();

            return TypeCache.FindType(typeName, typeArray);
        }

        /// <summary>
		/// (fn (exception) ...)
        /// Throws an exception
		/// </summary>
		/// <param name="args">A list containing the exception to throw</param>
		/// <param name="environment">The local environment</param>
		/// <returns>Does not return; throws an exception</returns>
        [Function("throw", "(exception)")]
		public static Object Throw(Environment environment) 
		{
            Object exc = environment.GetValue(SYSTEM, "exception");

            if (exc is Exception)
                throw (Exception)exc;
            else
                throw new YarrException(exc.ToString());
		}


		/// <summary>
		/// (fn (object) ...)
        /// Determine the runtime type of an object
		/// </summary>
		/// <param name="args">A list containing the object</param>
		/// <param name="environment">The local environment</param>
		/// <returns>The runtime type of the object</returns>
		[Function("typeof", "(obj)")]
        public static Object TypeOf(Environment environment) 
		{
            return Primitives.TypeOf(environment.GetValue(SYSTEM, "obj"));
		}

        /// <summary>
        /// (fn (object) ...)
        /// Determines if object is null
        /// </summary>
        /// <param name="args"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        [Function("nullp", "(obj)")]
        public static Object NullP (Environment environment)
        {
            return Primitives.IsNull(environment.GetValue(SYSTEM, "obj"));
        }

        /// <summary>
        /// (fn (package-names &optional ((pkg yarr.package)) ...)
        /// Adds packages to the list of used packages
        /// </summary>
        /// <param name="environment">The local environment</param>
        /// <returns>Always returns TRUE, unless an exception is thrown</returns>
        [Function("use-package", "(package-names &optional ((pkg yarr.package)))")]
        public static Object UsePackage(Environment environment)
        {
            Package pkg = (Package)environment.GetValue(SYSTEM, "pkg");
            if (pkg == null)
                pkg = Symbol.CurrentPackage;

            Object syms = environment.GetValue(SYSTEM, "package-names");

            if (syms is Symbol || syms is String)
            {
                Package usePkg = Package.FindPackage(syms.ToString());
                if (usePkg != null)
                    pkg.UsePackage(usePkg);
                else
                    throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "UsePackage: No such package: {0}", syms));
            }
            else if (syms is Cons)
            {
                foreach (Object s in (Cons)syms)
                {
                    if (s is Symbol || s is String)
                    {
                        Package usePkg = Package.FindPackage(s.ToString());
                        if (usePkg != null)
                            pkg.UsePackage(usePkg);
                        else
                            throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "UsePackage: No such package: {0}", s));
                    }
                    else
                        throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                                "UsePackage: Expecting package name, but received {0}", s));
                }
            }
            else
                throw new YarrException(String.Format(CultureInfo.CurrentCulture,
                                           "UsePackage: Expecting symbol name(s) but received {0}", syms));

            return true;
        }

		/// <summary>
        /// (fn (&rest namespaces) ...)
		/// Permit the use of types in a namespace, such that, you do not have to 
		/// qualify the use of a type in that namespace
		/// </summary>
		/// <param name="args">A list of namespace names</param>
		/// <param name="environment">The local environment</param>
		/// <returns></returns>
		[Function("using","(&rest namespaces)")]
        public static Object Using(Environment environment) 
		{
            Cons nameSpaces = (Cons)environment.GetValue(SYSTEM, "namespaces");
			string result = null;

            if (nameSpaces != null)
            {
                foreach (Object name in nameSpaces)
                {
                    object useName = (name is Symbol) ? name.ToString() : name;

                    if (useName is String)
                    {
                        result = (string)useName;
                        TypeCache.Using(result);
                    }
                    else
                        throw new YarrException(String.Format(CultureInfo.CurrentCulture, 
                                    "Using: {0} is not a namespace", name));
                }
            }

			return result;
		}
	}
}
